package net.minecraftforge.forge.tasks

import org.gradle.api.DefaultTask
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.TaskAction
import org.objectweb.asm.*

import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream

abstract class CheckExcs extends DefaultTask {
	@InputFile abstract RegularFileProperty getBinary()
	@InputFiles abstract ConfigurableFileCollection getExcs()
	
    @TaskAction
    protected void exec() {
		Util.init()
		def known = []
		binary.get().asFile.withInputStream { i ->
			new ZipInputStream(i).withCloseable { zin ->
				def visitor = new ClassVisitor(Opcodes.ASM9) {
					private String cls
					@Override
					public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
						this.cls = name
					}
					
					@Override
					public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
						known.add(this.cls + '.' + name + descriptor)
						super.visitMethod(access, name, descriptor, signature, exceptions)
					}
				}
				ZipEntry zein
				while ((zein = zin.nextEntry) != null) {
					if (zein.name.endsWith('.class')) {
						ClassReader reader = new ClassReader(zin)
						reader.accept(visitor, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES)
					}
				}
			}
		}
		
		excs.each { f -> 
			def lines = []
			f.eachLine { line ->
				def idx = line.indexOf('#')
				if (idx == 0 || line.isEmpty()) {
					return
				}
				
				def comment = idx == -1 ? null : line.substring(idx)
				if (idx != -1) line = line.substring(0, idx - 1)
				
				if (!line.contains('=')) {
					println('Invalid: ' + line)
					return
				}
				
				def (key, value) = line.split('=', 2)
				if (!known.contains(key)) {
					println('Unknown: ' + line)
					return
				}
				
				def (cls, desc) = key.split('\\.', 2)
				if (!desc.contains('(')) {
					println('Invalid: ' + line)
					return
				}
				def name = desc.split('\\(', 2)[0]
				desc = '(' + desc.split('\\(', 2)[1]
				
				def (exceptions, args) = value.contains('|') ? value.split('|', 2) : [value, '']
				
				if (args.split(',').length != Type.getArgumentTypes(desc).length) {
					println('Invalid: ' + line)
					return
				}
				lines.add(line)
			}
			f.text = lines.sort().join('\n')
		}
	}
}
